#include "cst92f2x.h"
#include "rtc.h"
#include "pwrmgr.h"
#include "uart.h"
#include "OSAL_Clock.h"
#include "clock.h"
#include "log.h"
#include "global_config.h"
#include "jump_function.h"


#define UTC_START_TIME      946656000   //2000.1.1 0:0:0

#define UPDATA_TICK         300000000   //updata interval tick(unit:s)(300s)
#define RTC_COUNT_OF_5MIN   9830400     //32768*5*60
#define RTC_COUNT_OF_12HOUR 1415577600  //32768*12*60*60
#define RTC_COUNT_OF_12HOUR_1MIN 1417543680  //32768*(13*60+1)*60 12HOUR+1MIN

/**
* @struct RtcCtl_t
* @brief RTC control information
*/
typedef struct {
	uint64_t prevUpdateBase;
	uint32_t prevUpdateTicks;
    uint32_t lastTicks;
}RtcCtl_t;

static RtcCtl_t rtcCtl;

static uint32_t tick_unit = 0;
static uint32_t rtc_cnt_track=0;

uint32_t get_rtc_counter(void)
{
    uint32_t cnt0,cnt1;

	while(1)
	{
		cnt0 =((*(volatile unsigned int *)0x4000f028) & 0xFFFFFFFF);
		cnt1 =((*(volatile unsigned int *)0x4000f028) & 0xFFFFFFFF);

		if(cnt1==cnt0)
			break;
	}

	return cnt1;
}

/**
* @fn static void HalRtcUpdate(void)
* @brief Update RTC time. this function or HalRtcGetTime(UTCTimeStruct *getTime) function should be called periodically, the periodic time < 512s, recommend 60s  
* @param none.
* @return none
*/
static void HalRtcUpdate(void)
{
	uint32_t tick;
    uint32_t delta_time;
    uint32_t temp;
    uint32_t cnt_track;
    
	tick = get_rtc_counter();
    
    if(pGlobal_config[LL_SWITCH] & RC32_TRACKINK_ALLOW)
    {
        delta_time = tick - rtcCtl.lastTicks;
        temp = ((((delta_time &0xffff0000)>>16)*rtc_cnt_track)<<8)
              +(((delta_time &0xffff)*rtc_cnt_track)>>8);
        rtcCtl.prevUpdateTicks = temp;
        if(temp > UPDATA_TICK)//5min
        {
            cnt_track = *(volatile uint32_t *)0x4000f064 & 0x1ffff;
            rtc_cnt_track =  (cnt_track>CRY32_16_CYCLE_16MHZ_CYCLE_MAX || cnt_track<CRY32_16_CYCLE_16MHZ_CYCLE_MIN)
                                    ? rtc_cnt_track : cnt_track;
            
            rtcCtl.lastTicks = tick;
            rtcCtl.prevUpdateBase += temp;
            rtcCtl.prevUpdateTicks = 0;
        }
        RTC_TIMEBASE = rtcCtl.prevUpdateBase+rtcCtl.prevUpdateTicks;
    }
    else
    {
        if(tick < rtcCtl.prevUpdateTicks)
        {
            if( ((uint32_t)(tick-rtcCtl.prevUpdateTicks)) < RTC_COUNT_OF_12HOUR_1MIN )
            {
                rtcCtl.prevUpdateBase += RTC_CNT_RANGE;
            }
        }
        rtcCtl.prevUpdateTicks = tick;
        RTC_TIMEBASE = rtcCtl.prevUpdateBase;
    }
//    LOG("track:%x\r\n", rtc_cnt_track);
//    LOG("base:%x%08x\r\n", (uint32)((uint64_t)rtcCtl.prevUpdateBase>>32), ((uint32)rtcCtl.prevUpdateBase&0xFFFFFFFF));
//    LOG("tick:%x,last:%x\r\n", tick, rtcCtl.lastTicks);
//    LOG("prev:%x\r\n", rtcCtl.prevUpdateTicks);
//    LOG("%d\r\n", tick);
}
/**
* @fn void HalRtcGetTime(UTCTimeStruct *getTime)
* @brief Get RTC time. this function or HalRtcUpdate(void) function should be called periodically, the periodic time < 512s, recommend 60s  
* @param getTime: current time output value.
* @return none
*/
void HalRtcGetTime(UTCTimeStruct *getTime)
{
	uint64_t tickSum;
	uint32_t time;
	HalRtcUpdate();
	tickSum = rtcCtl.prevUpdateTicks + rtcCtl.prevUpdateBase;
	time = tickSum/tick_unit;
	osal_ConvertUTCTime(getTime, time);
}
/**
* @fn void HalRtcSetTime(UTCTimeStruct *setTime)
* @brief Set RTC time. 
* @param setTime: time set value.
* @return none
*/
void HalRtcSetTime(UTCTimeStruct *setTime)
{
    rtcCtl.prevUpdateBase = (uint64_t)osal_ConvertUTCSecs(setTime)*tick_unit;
    if(pGlobal_config[LL_SWITCH] & RC32_TRACKINK_ALLOW)
    {
        rtcCtl.prevUpdateTicks = 0;
        rtcCtl.lastTicks = get_rtc_counter();
    }
    else
    {
        rtcCtl.prevUpdateTicks = get_rtc_counter();
    }
    
    rtcCtl.prevUpdateBase -= rtcCtl.prevUpdateTicks;
	REG_RTC_CC2 = 0xAA55AA;
	RTC_TIMEBASE = rtcCtl.prevUpdateBase;
}

/**
* @fn UTCTime HalRtcGet_Utc(void)
* @brief Get RTC time. this function or HalRtcUpdate(void) function should be called periodically, the periodic time < 512s, recommend 60s  
* @param getTime: current time output value.
* @return none
*/
UTCTime HalRtcGet_Utc(void)
{
	uint64_t tickSum;
	uint32_t time;
	HalRtcUpdate();
	tickSum = rtcCtl.prevUpdateTicks + rtcCtl.prevUpdateBase;
	time = tickSum/tick_unit+UTC_START_TIME;
	return time;
}

/**
* @fn void HalRtcSet_Utc(UTCTime utc)
* @brief Set RTC time. 
* @param setTime: time set value.
* @return none
*/
void HalRtcSet_Utc(UTCTime utc)
{
	rtcCtl.prevUpdateBase = (uint64_t)(utc-UTC_START_TIME)*tick_unit;
    if(pGlobal_config[LL_SWITCH] & RC32_TRACKINK_ALLOW)
    {
        rtcCtl.prevUpdateTicks = 0;
        rtcCtl.lastTicks = get_rtc_counter();
    }
    else
    {
        rtcCtl.prevUpdateTicks = get_rtc_counter();
    }
    
    rtcCtl.prevUpdateBase -= rtcCtl.prevUpdateTicks;
	REG_RTC_CC2 = 0xAA55AA;
	RTC_TIMEBASE = rtcCtl.prevUpdateBase;
}

void __attribute__((used)) HalRtcIRQHandler(void)
{
    if(pGlobal_config[LL_SWITCH] & RC32_TRACKINK_ALLOW)
    {
        *(volatile uint32_t *) 0x4000f030 = (get_rtc_counter() + RTC_COUNT_OF_5MIN)&0xFFFFFFFF;
    }
    else
    {
        *(volatile uint32_t *) 0x4000f030 = (get_rtc_counter() + RTC_COUNT_OF_12HOUR)&0xFFFFFFFF;
    }
    HalRtcUpdate();
}

void HalRtcWakeupHandler(void)
{
    NVIC_EnableIRQ((IRQn_Type)RTC_IRQn);
    HalRtcUpdate();
}

/**
 * @brief
 * @param  psetTime   if soft reset don't set rtc
 *                     else if psetTime is null reset rtc as defualt
 *                     else set rtc as psettime value
 */
void HalRtcInit(UTCTimeStruct *psetTime)
{
    uint32_t rtcValue;
    UTCTimeStruct setTime;

    if (pGlobal_config[LL_SWITCH] & RC32_TRACKINK_ALLOW)
    {
        tick_unit = US_OF_S;
    }
    else
    {
        tick_unit = RTC_CLK_FREQ;
    }

    rtcValue = REG_RTC_CC2 & 0xffffff;
    LOG("rtcValue:%04x\r\n", rtcValue);
    if (rtcValue != 0xAA55AA) // RTC don't be initized, init RTC
    {
        if (psetTime == NULL) // set time as Default
        {
            // set time:2019.1.1 00:00:00
            setTime.year = 2019;
            setTime.month = 0;
            setTime.day = 0;
            setTime.hour = 0;
            setTime.minutes = 0;
            setTime.seconds = 0;
        }
        else
        {
            osal_memcpy(&setTime, psetTime, sizeof(setTime));
        }
        HalRtcSetTime(&setTime);
        LOG("InitTime:%d/%d/%d %d:%d:%d\r\n", setTime.year, setTime.month + 1, setTime.day + 1, setTime.hour, setTime.minutes, setTime.seconds);
    }
    else // system reset
    {
        rtcCtl.lastTicks = get_rtc_counter();
        rtcCtl.prevUpdateTicks = get_rtc_counter();
        rtcCtl.prevUpdateBase = RTC_TIMEBASE;
        HalRtcGetTime(&setTime);
        LOG("recover:%d/%d/%d %d:%d:%d\r\n", setTime.year, setTime.month + 1, setTime.day + 1, setTime.hour, setTime.minutes, setTime.seconds);
    }

    rtc_cnt_track = *(volatile uint32_t *)0x4000f064 & 0x1ffff;
    rtc_cnt_track = (rtc_cnt_track > CRY32_16_CYCLE_16MHZ_CYCLE_MAX || rtc_cnt_track < CRY32_16_CYCLE_16MHZ_CYCLE_MIN)
                        ? STD_RC32_16_CYCLE_16MHZ_CYCLE
                        : rtc_cnt_track;

    if (pGlobal_config[LL_SWITCH] & RC32_TRACKINK_ALLOW)
    {
        *(volatile uint32_t *)0x4000f030 = (get_rtc_counter() + RTC_COUNT_OF_5MIN) & 0xFFFFFFFF;
    }
    else
    {
        *(volatile uint32_t *)0x4000f030 = (get_rtc_counter() + RTC_COUNT_OF_12HOUR) & 0xFFFFFFFF;
    }

    JUMP_FUNCTION(RTC_IRQ_HANDLER) = (uint32_t)&HalRtcIRQHandler;
    *(volatile uint32_t *)0x4000f024 |= BIT(16) | BIT(21);
    NVIC_SetPriority((IRQn_Type)RTC_IRQn, IRQ_PRIO_HAL);
    NVIC_EnableIRQ((IRQn_Type)RTC_IRQn);

    hal_pwrmgr_register(MOD_RTC, NULL, HalRtcWakeupHandler);
}






